#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Geometry/Volumes/sphere.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! Launch the unit tests on Sphere objects
/*!
 @return True if all unit tests on Sphere objects were
         succesful, false otherwise
 */
template <class T>
bool UnitTest::test_geometry_sphere_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Create line
	GameEngine::Point3d<T> center((T)0,(T)0,(T)0);
	T radius = (T)5;
	GameEngine::Sphere<T> sphere(center,radius);

	// Test accessors
	T tolerance = (T)0.00001;
	{{
		res &= UNIT_TEST_ERROR( "[Sphere] Test center()", ( center.get_square_distance(sphere.center()) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test radius()", ( abs(sphere.radius()-radius) < tolerance ) );
	}}

	// Test diameter, area and volume functions
	{{
		res &= UNIT_TEST_ERROR( "[Sphere] Test diameter()", ( abs(sphere.diameter()-(T)2*radius) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test area()", ( abs(sphere.area()-(T)(4.0*PI)*radius*radius) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test volume()", ( abs(sphere.volume()-(T)(4.0*PI*radius*radius*radius/3.0)) < tolerance ) );
	}}

	// Test inside function
	{{
		GameEngine::Point3d<T> p1((T)1.5,(T)0.5,(T)0.3);
		GameEngine::Point3d<T> p2((T)5.5,(T)0.5,(T)0.7);
		GameEngine::Point3d<T> p3((T)radius,(T)0.0,(T)0.0);
		res &= UNIT_TEST_ERROR( "[Sphere] Test inside()", ( sphere.inside(p1) == true ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test inside()", ( sphere.inside(p2) == false ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test inside()", ( sphere.inside(p3, true) == false ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test inside()", ( sphere.inside(p3, false) == true ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,(T)0,(T)0);
		GameEngine::Point3d<T> p2((T)1,(T)1,(T)0);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( sphere.intersection(line, coord1, coord2, &type) == 2 ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( abs(coord1-radius/(T)sqrt(2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( abs(coord2+radius/(T)sqrt(2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( type == GameEngine::IntersectionType::Intersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,radius,(T)0);
		GameEngine::Point3d<T> p2((T)1,radius,(T)0);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( sphere.intersection(line, coord1, coord2, &type) == 1 ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( coord1 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( coord2 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( type == GameEngine::IntersectionType::TangentIntersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,radius+(T)1,(T)0);
		GameEngine::Point3d<T> p2((T)1,radius+(T)1,(T)0);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( sphere.intersection(line, coord1, coord2, &type) == 0 ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test intersection()", ( type == GameEngine::IntersectionType::NoIntersection ) );
	}}

	// Test closest point function
	{{
		GameEngine::Point3d<T> pt((T)10,(T)10,(T)0);
		GameEngine::Point3d<T> cpt = sphere.closest_point(pt);
		res &= UNIT_TEST_ERROR( "[Sphere] Test closest_point()", ( sphere.closest_point(pt).get_square_distance(
			GameEngine::Point3d<T>((T)sqrt(2.0)*radius/(T)2,(T)sqrt(2.0)*radius/(T)2,(T)0)) < tolerance ) );
	}}

	// Test point function
	{{
		GameEngine::Angle<T> az1((T)0, GameEngine::Angles::Deg);
		GameEngine::Angle<T> az2((T)90, GameEngine::Angles::Deg);
		GameEngine::Angle<T> az3((T)180, GameEngine::Angles::Deg);
		GameEngine::Angle<T> az4((T)270, GameEngine::Angles::Deg);
		res &= UNIT_TEST_ERROR( "[Sphere] Test point()", ( sphere.point(az1,az1).get_square_distance(GameEngine::Point3d<T>(radius,(T)0,(T)0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test point()", ( sphere.point(az2,az1).get_square_distance(GameEngine::Point3d<T>((T)0,radius,(T)0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test point()", ( sphere.point(az3,az1).get_square_distance(GameEngine::Point3d<T>(-radius,(T)0,(T)0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Sphere] Test point()", ( sphere.point(az4,az1).get_square_distance(GameEngine::Point3d<T>((T)0,-radius,(T)0)) < tolerance ) );
	}}

	// Get number of bytes
	res &= UNIT_TEST_ERROR( "[Sphere] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
